package mylittleinvestigations.PersistentThreads;

import java.awt.FontFormatException;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.security.AccessController;
import java.security.PrivilegedAction;
import java.util.LinkedList;
import java.util.Queue;
import java.util.concurrent.Semaphore;
import java.util.logging.Level;
import java.util.logging.Logger;
import mylittleinvestigations.CaseInformation.Case;
import mylittleinvestigations.Confrontation;
import mylittleinvestigations.Debug;
import mylittleinvestigations.Dialog;
import mylittleinvestigations.Encounter;
import mylittleinvestigations.EvidenceSelector;
import mylittleinvestigations.Game;
import mylittleinvestigations.GameUi.*;
import mylittleinvestigations.GameUi.GameUiLoader;
import mylittleinvestigations.Notification;
import mylittleinvestigations.Settings;
import mylittleinvestigations.Utils;
import org.dawnmew.jvirtualstream.VirtualFileSystem;
import org.lwjgl.LWJGLException;
import org.lwjgl.opengl.Display;
import org.lwjgl.opengl.DisplayMode;
import org.lwjgl.opengl.Drawable;
import org.lwjgl.opengl.Pbuffer;
import org.lwjgl.opengl.PixelFormat;
import org.newdawn.slick.BigImage;
import org.newdawn.slick.Color;
import org.newdawn.slick.Font;
import org.newdawn.slick.GameContainer;
import org.newdawn.slick.Graphics;
import org.newdawn.slick.Image;
import org.newdawn.slick.SlickException;
import org.newdawn.slick.geom.Polygon;
import org.newdawn.slick.opengl.PNGImageData;
import org.newdawn.slick.opengl.renderer.Renderer;
import org.newdawn.slick.opengl.renderer.SGL;
import org.newdawn.slick.util.Log;

public class RenderThread implements Runnable
{
    private static SGL GL = Renderer.get();
    
    private static RenderThread instance;
    
    public static RenderThread getInstance()
    {
        if (instance == null)
        {
            instance = new RenderThread();
        }
        
        return instance;
    }
    
    private static Drawable sharedContext;
    
    public static Drawable getSharedContext()
    {
        /*if (sharedContext == null)
        {
            try
            {
                sharedContext = new Pbuffer(64, 64, new PixelFormat(8, 0, 0), null);
            }
            catch (LWJGLException ex)
            {
                Logger.getLogger(RenderThread.class.getName()).log(Level.SEVERE, null, ex);
            }
        }
        
        return sharedContext;*/
        return GameContainer.getSharedContext();
    }
    
    private Thread renderThread;
    private Graphics defaultGraphics;
    private Graphics graphics;
    private Graphics offscreenGraphics;
    private Font defaultFont;
    
    private Game game;
    private int screenWidth;
    private int screenHeight;
    
    private volatile boolean isInitialized;
    private volatile boolean isRunning;
    private volatile boolean doneLoadingImages;
    private int recordedFps;
    
    private Queue<DrawableObject> renderQueue;
    private Queue<DrawableObject> loadingQueue;
    private Queue<DrawableObject> finalizedRenderQueue;
    private Queue<DrawableObject> finalizedLoadingQueue;
    private Queue<DrawableObject> inProgressRenderQueue;
    private final Object finalizedRenderQueueSyncObject = new Object();
    private final Object finalizedLoadingQueueSyncObject = new Object();
    private Semaphore finalizedRenderQueueSemaphore;
    private Semaphore finalizedRenderQueuePickedUpSemaphore;
    private Semaphore destroyWaitSemaphore;
    private Semaphore afterDestroyWaitSemaphore;
    
    private RenderThread()
    {
        this.renderQueue = new LinkedList<DrawableObject>();
        this.loadingQueue = new LinkedList<DrawableObject>();
        this.isInitialized = false;
    }
    
    public void Init(Game game, int screenWidth, int screenHeight)
    {
        this.game = game;
        this.screenWidth = screenWidth;
        this.screenHeight = screenHeight;
        this.isInitialized = false;
        this.isRunning = true;
        this.doneLoadingImages = false;
        this.finalizedRenderQueueSemaphore = new Semaphore(0);
        this.finalizedRenderQueuePickedUpSemaphore = new Semaphore(0);
        this.destroyWaitSemaphore = new Semaphore(0);
        this.afterDestroyWaitSemaphore = new Semaphore(0);
        
        this.renderThread = new Thread(this);
        this.renderThread.setName("MLI Render Thread");
        this.renderThread.setPriority(Thread.NORM_PRIORITY);
        this.renderThread.start();
    }
    
    public void InitGraphics(Graphics g)
    {
        this.defaultGraphics = g;
        this.graphics = g;
    }
    
    public boolean IsReady()
    {
        return this.isInitialized;
    }
    
    public boolean IsClosed()
    {
        return !this.isRunning;
    }
    
    public boolean DoneLoadingImages()
    {
        return this.doneLoadingImages;
    }
    
    public void LoadGameUi()
    {
        this.loadingQueue.add(new GameUiToLoad());
    }
    
    public void LoadImage(String textureId, String filePath)
    {
        this.loadingQueue.add(new DrawableImageForLoad(textureId, filePath));
    }
    
    public void LoadImage(Image image)
    {
        this.loadingQueue.add(new DrawableImageToBeLoaded(image));
    }
    
    public void AddImageToRenderQueue(Image image, float x, float y)
    {
        this.AddImageToRenderQueue(image, x, y, Color.white);
    }
    
    public void AddImageToRenderQueue(Image image, float x, float y, Color color)
    {
        this.renderQueue.add(new DrawableImage(image, x, y, color));
    }
    
    public void AddImageToRenderQueue(Image image, float x, float y, Color color, boolean flipHorizontally, boolean flipVertically)
    {
        this.renderQueue.add(new DrawableFlippedImage(image, x, y, color, flipHorizontally, flipVertically));
    }
    
    public void AddImageToRenderQueue(
        Image image,
        float x,
        float y,
        int clipRectX,
        int clipRectY,
        int clipRectWidth,
        int clipRectHeight,
        boolean flipHorizontally,
        boolean flipVertically,
        float scale,
        Color color)
    {
        this.renderQueue.add(
                new DrawableManipulatedImage(
                        image,
                        x,
                        y,
                        clipRectX,
                        clipRectY,
                        clipRectWidth,
                        clipRectHeight,
                        flipHorizontally,
                        flipVertically,
                        scale,
                        color));
    }
    
    public void AddStringToRenderQueue(Font font, float x, float y, String s)
    {
        this.AddStringToRenderQueue(font, x, y, s, Color.white);
    }
    
    public void AddStringToRenderQueue(Font font, float x, float y, String s, Color color)
    {
        this.renderQueue.add(new DrawableString(font, x, y, s, color));
    }
    
    public void AddPolygonToRenderQueue(Polygon polygon, Color color)
    {
        this.renderQueue.add(new DrawablePolygon(polygon, color));
    }
    
    public void UseIntermediateRenderTarget(Graphics intermediateRenderTarget)
    {
        this.renderQueue.add(new UseIntermediateRenderTarget(intermediateRenderTarget));
    }
    
    public void FinishUsingIntermediateRenderTarget()
    {
        this.renderQueue.add(new FinishUsingIntermediateRenderTarget());
    }
    
    public void FinalizeRenderQueue()
    {
        synchronized (this.finalizedRenderQueueSyncObject)
        {
            this.finalizedRenderQueue = this.renderQueue;
            this.renderQueue = new LinkedList<DrawableObject>();
        }
    }
    
    public void FinalizeImagesToLoad()
    {
        this.loadingQueue.add(new ScreenClearAfterLoading());
            
        synchronized (this.finalizedLoadingQueueSyncObject)
        {
            this.finalizedLoadingQueue = this.loadingQueue;
            this.loadingQueue = new LinkedList<DrawableObject>();
        }
    }
    
    public void SetFps(int fps)
    {
        this.recordedFps = fps;
    }
    
    public void NotifyDestroyAllowed()
    {
        this.destroyWaitSemaphore.release();
    }
    
    public void WaitForDestroy()
    {
        try
        {
            this.afterDestroyWaitSemaphore.acquire();
        }
        catch (InterruptedException ex)
        {
            Logger.getLogger(RenderThread.class.getName()).log(Level.SEVERE, null, ex);
        }
    }
    
    public void DrawEverything()
    {
        synchronized (this.finalizedLoadingQueueSyncObject)
        {
            this.inProgressRenderQueue = new LinkedList<DrawableObject>();
            
            if (this.finalizedLoadingQueue != null)
            {
                this.inProgressRenderQueue.addAll(this.finalizedLoadingQueue);
            }
            
            if (this.finalizedRenderQueue != null)
            {
                this.inProgressRenderQueue.addAll(this.finalizedRenderQueue);
            }
            
            this.finalizedLoadingQueue = null;
            this.finalizedRenderQueue = null;
        }

        if (this.inProgressRenderQueue != null)
        {
            boolean imageLoadingInProgress = false;
            
            for (DrawableObject drawableObject : this.inProgressRenderQueue)
            {
                drawableObject.Draw(this);

                // If we're loading images, we need to flag the fact
                // that image loading is in progress.
                if (drawableObject instanceof DrawableImageToBeLoaded || drawableObject instanceof DrawableImageForLoad)
                {
                    imageLoadingInProgress = true;
                }
            }

            if (imageLoadingInProgress)
            {
                this.doneLoadingImages = true;
            }
        }
    }

    public void run()
    {
        try
        {
            Display.setDisplayMode(new DisplayMode(this.screenWidth, this.screenHeight));
            Display.setFullscreen(false);
            Display.setTitle(this.game.getTitle());

            AccessController.doPrivileged(new PrivilegedAction()
            {
                public Object run()
                {
                    try
                    {
                        Display.create(new PixelFormat(8, 8, 8, 0), getSharedContext());
                    }
                    catch (Exception e)
                    {
                        Display.destroy();

                        try
                        {
                            Display.create(new PixelFormat(), getSharedContext());
                        }
                        catch (Exception e2)
                        {
                            Log.error(e2);
                        }
                    }

                    return null;
                }
            });
		
            if (!Display.isCreated())
            {
                throw new SlickException("Failed to initialise the LWJGL display");
            }
            
            GL.initDisplay(this.screenWidth, this.screenHeight);
            this.graphics = new Graphics(this.screenWidth, this.screenHeight);
            this.offscreenGraphics = new Graphics(2048, 2048);
            this.defaultGraphics = this.graphics;
            this.defaultFont = this.graphics.getFont();
            GL.enterOrtho(this.screenWidth, this.screenHeight);
            
            try
            {
                java.awt.Font architectsDaughterFont = java.awt.Font.createFont(java.awt.Font.TRUETYPE_FONT, VirtualFileSystem.getInstance().getInputStream("fonts/ArchitectsDaughter.ttf"));

                GameUiLoader.getInstance().dialogFont = Utils.getUnicodeFont(architectsDaughterFont, 18, true, false);
                GameUiLoader.getInstance().evidenceSelectorFont = Utils.getUnicodeFont(architectsDaughterFont, 9, true, false);
                GameUiLoader.getInstance().partnerInformationFont = Utils.getUnicodeFont(architectsDaughterFont, 7, true, false);

                java.awt.Font celestiaMediumFont = java.awt.Font.createFont(java.awt.Font.TRUETYPE_FONT, VirtualFileSystem.getInstance().getInputStream("fonts/CelestiaMediumRedux1.5.ttf"));

                GameUiLoader.getInstance().confrontationNumberingFont = Utils.getUnicodeFont(celestiaMediumFont, 100, false, false);
                GameUiLoader.getInstance().tabFont = Utils.getUnicodeFont(celestiaMediumFont, 16, false, false);
            }
            catch (FontFormatException ex)
            {
                Logger.getLogger(Game.class.getName()).log(Level.SEVERE, null, ex);
            }
            catch (IOException ex)
            {
                Logger.getLogger(Game.class.getName()).log(Level.SEVERE, null, ex);
            }
            
            this.isInitialized = true;
            
            while (this.isRunning)
            {
                if (Settings.UpdateOnlyWhenVisible && !Display.isVisible())
                {
                    try
                    {
                        Thread.sleep(100);
                    }
                    catch (Exception e)
                    {
                    }
                }
                else
                {
                    try
                    {
                        this.finalizedRenderQueueSemaphore.acquire();
                    }
                    catch (InterruptedException ex)
                    {
                        Logger.getLogger(RenderThread.class.getName()).log(Level.SEVERE, null, ex);
                    }
                    
                    this.inProgressRenderQueue = this.finalizedRenderQueue;
                    this.finalizedRenderQueue = null;

                    if (this.finalizedRenderQueuePickedUpSemaphore.availablePermits() == 0)
                    {
                        this.finalizedRenderQueuePickedUpSemaphore.release();
                    }
                    
                    if (this.inProgressRenderQueue != null)
                    {
                        boolean imageLoadingInProgress = false;
                        
                        if (Settings.ClearEachFrame)
                        {
                            GL.glClear(SGL.GL_COLOR_BUFFER_BIT | SGL.GL_DEPTH_BUFFER_BIT);
                        } 

                        GL.glLoadIdentity();

                        this.graphics.resetTransform();
                        this.graphics.resetFont();
                        this.graphics.resetLineWidth();
                        this.graphics.setAntiAlias(false);

                        for (DrawableObject drawableObject : this.inProgressRenderQueue)
                        {
                            drawableObject.Draw(this);
                            
                            // If we're loading images, we need to flag the fact
                            // that image loading is in progress.
                            if (drawableObject instanceof DrawableImageToBeLoaded || drawableObject instanceof DrawableImageForLoad)
                            {
                                imageLoadingInProgress = true;
                            }
                        }

                        this.graphics.resetTransform();

                        if (Debug.ShowFps)
                        {
                            this.defaultFont.drawString(10, 10, "FPS: " + this.recordedFps);
                        }

                        GL.flush();
                
                        Display.update();
                        InputThread.getInstance().PollInput();

                        if (Display.isCloseRequested())
                        {
                            if (this.game.closeRequested())
                            {
                                this.isRunning = false;
                            }
                        }
                        
                        if (imageLoadingInProgress)
                        {
                            this.doneLoadingImages = true;
                        }
                    }
                }
            }
        }
        catch (LWJGLException ex)
        {
            Logger.getLogger(RenderThread.class.getName()).log(Level.SEVERE, null, ex);
        }
        catch (SlickException ex)
        {
            Logger.getLogger(RenderThread.class.getName()).log(Level.SEVERE, null, ex);
        }
        finally
        {
            this.isRunning = false;
            
            // We'll give this semaphore an effectively infinite number of permits,
            // in order to make sure nobody blocks on it anymore.
            this.finalizedRenderQueuePickedUpSemaphore.release(Integer.MAX_VALUE);
            
            try
            {
                this.destroyWaitSemaphore.acquire();
            }
            catch (InterruptedException ex)
            {
                Logger.getLogger(RenderThread.class.getName()).log(Level.SEVERE, null, ex);
            }
            
            Display.destroy();
            
            this.afterDestroyWaitSemaphore.release();
        }
    }
    
    private interface DrawableObject
    {
        public void Draw(RenderThread renderThread);
    }
    
    private class GameUiToLoad implements DrawableObject
    {
        public GameUiToLoad()
        {
        }
        
        public void Draw(RenderThread renderThread)
        {
            try
            {
                Arrow.Initialize(new Image(VirtualFileSystem.getInstance().getInputStream("image/ArrowHorizontal.png"), "image/ArrowHorizontal.png", false), new Image(VirtualFileSystem.getInstance().getInputStream("image/ArrowVertical.png"), "image/ArrowVertical.png", false));

                // +3 to overlap the tab's border with the dialog background's border.
                Tab.Initialize((int)Settings.DisplayBounds.getY() - Dialog.Height - Tab.Height + 3, (int)Settings.DisplayBounds.getY() - Tab.Height, new Image(VirtualFileSystem.getInstance().getInputStream("image/Tab.png"),"image/Tab.png", false), new Image(VirtualFileSystem.getInstance().getInputStream("image/TabTop.png"), "image/TabTop.png", false), GameUiLoader.getInstance().tabFont);
                Button.Initialize(GameUiLoader.getInstance().dialogFont, new Image(VirtualFileSystem.getInstance().getInputStream("image/CheckMark.png"), "image/CheckMark.png", false), new Image(VirtualFileSystem.getInstance().getInputStream("image/Lock.png"), "image/Lock.png", false));

                ButtonArray.Initialize(
                    3, 363, 954, 174,
                    30,
                    new Image(VirtualFileSystem.getInstance().getInputStream("image/EncounterPrompt.png"), "image/EncounterPrompt.png", false),
                    GameUiLoader.getInstance().tabFont,
                    GameUiLoader.getInstance().dialogFont);

                EvidenceSelector.Initialize(
                    new Image(VirtualFileSystem.getInstance().getInputStream("image/EvidenceSelectorItem.png"), "image/EvidenceSelectorItem.png", false),
                    new Image(VirtualFileSystem.getInstance().getInputStream("image/EvidenceSelectorItemSelection.png"), "image/EvidenceSelectorItemSelection.png", false),
                    new Image(VirtualFileSystem.getInstance().getInputStream("image/EvidenceSelectorDescription.png"), "image/EvidenceSelectorDescription.png", false),
                    GameUiLoader.getInstance().evidenceSelectorFont);

                Dialog.Initialize(
                    3, 363, 954, 174,
                    30,
                    GameUiLoader.getInstance().dialogFont);

                Notification.Initialize(
                    3, 363, 954, 174,
                    30,
                    GameUiLoader.getInstance().dialogFont);

                Confrontation.Initialize(
                    new Image(VirtualFileSystem.getInstance().getInputStream("image/ConfrontationHealthCircle.png"), "image/ConfrontationHealthCircle.png", false),
                    GameUiLoader.getInstance().confrontationNumberingFont);
                Encounter.Initialize(
                    new Image(VirtualFileSystem.getInstance().getInputStream("image/DialogBackground.png"), "image/DialogBackground.png", false),
                    new Image(VirtualFileSystem.getInstance().getInputStream("image/EncounterDarkBackground.png"), "image/EncounterDarkBackground.png", false));
                PartnerInformation.Initialize(
                    new Image(VirtualFileSystem.getInstance().getInputStream("image/PartnerInfoBackground.png"), "image/PartnerInfoBackground.png",false),
                    GameUiLoader.getInstance().tabFont,
                    GameUiLoader.getInstance().partnerInformationFont);

                GameUiLoader.getInstance().isUiLoaded = true;
            }
            catch (SlickException ex)
            {
                Logger.getLogger(RenderThread.class.getName()).log(Level.SEVERE, null, ex);
            }
        }
    }
    
    private class DrawableImageForLoad implements DrawableObject
    {
        private String textureId;
        private String filePath;
        
        public DrawableImageForLoad(String textureId, String filePath)
        {
            this.textureId = textureId;
            this.filePath = filePath;
        }
        
        public void Draw(RenderThread renderThread)
        {
            try
            {
                PNGImageData imageData = new PNGImageData();
                ByteBuffer byteBuffer = imageData.loadImage(VirtualFileSystem.getInstance().getInputStream(filePath));
                
                BigImage bigImage = new BigImage(imageData, byteBuffer, Image.FILTER_LINEAR);
                Case.getInstance().getSpriteManager().AddTexture(this.textureId, bigImage);
                
                for (int y = 0; y < bigImage.getVerticalImageCount(); y++)
                {
                    for (int x = 0; x < bigImage.getHorizontalImageCount(); x++)
                    {
                        bigImage.getTile(x, y).draw(0, 0);
                        
                        try
                        {
                            Thread.sleep(10);
                        }
                        catch (InterruptedException ex)
                        {
                            Logger.getLogger(RenderThread.class.getName()).log(Level.SEVERE, null, ex);
                        }
                        
                        System.gc();
                    }
                }
            }
            catch (IOException ex)
            {
                Logger.getLogger(RenderThread.class.getName()).log(Level.SEVERE, null, ex);
            }
        }
    }
    
    private class DrawableImageToBeLoaded implements DrawableObject
    {
        private Image image;
        
        public DrawableImageToBeLoaded(Image image)
        {
            this.image = image;
        }
        
        public void Draw(RenderThread renderThread)
        {
            renderThread.graphics.drawImage(this.image, 0, 0);
                        
            try
            {
                Thread.sleep(10);
            }
            catch (InterruptedException ex)
            {
                Logger.getLogger(RenderThread.class.getName()).log(Level.SEVERE, null, ex);
            }
        }
    }
    
    private class ScreenClearAfterLoading implements DrawableObject
    {
        public ScreenClearAfterLoading()
        {
        }
        
        public void Draw(RenderThread renderThread)
        {
            GL.glClear(SGL.GL_COLOR_BUFFER_BIT);
        }
    }
    
    private class DrawableImage implements DrawableObject
    {
        private Image image;
        private float x;
        private float y;
        private Color color;
        
        public DrawableImage(Image image, float x, float y, Color color)
        {
            this.image = image;
            this.x = x;
            this.y = y;
            this.color = color;
        }
        
        public void Draw(RenderThread renderThread)
        {
            renderThread.graphics.drawImage(this.image, this.x, this.y, this.color);
        }
    }
    
    private class DrawableFlippedImage implements DrawableObject
    {
        private Image image;
        private float x;
        private float y;
        private Color color;
        private boolean flipHorizontally;
        private boolean flipVertically;
        
        public DrawableFlippedImage(Image image, float x, float y, Color color, boolean flipHorizontally, boolean flipVertically)
        {
            this.image = image;
            this.x = x;
            this.y = y;
            this.color = color;
            this.flipHorizontally = flipHorizontally;
            this.flipVertically = flipVertically;
        }
        
        public void Draw(RenderThread renderThread)
        {
            renderThread.graphics.drawImage(this.image.getFlippedCopy(this.flipHorizontally, this.flipVertically), this.x, this.y, this.color);
        }
    }
    
    private class DrawableManipulatedImage implements DrawableObject
    {
        private Image image;
        private float x;
        private float y;
        private int clipRectX;
        private int clipRectY;
        private int clipRectWidth;
        private int clipRectHeight;
        private boolean flipHorizontally;
        private boolean flipVertically;
        private float scale;
        private Color color;
        
        public DrawableManipulatedImage(
                Image image,
                float x,
                float y,
                int clipRectX,
                int clipRectY,
                int clipRectWidth,
                int clipRectHeight,
                boolean flipHorizontally,
                boolean flipVertically,
                float scale,
                Color color)
        {
            this.image = image;
            this.x = x;
            this.y = y;
            this.clipRectX = clipRectX;
            this.clipRectY = clipRectY;
            this.clipRectWidth = clipRectWidth;
            this.clipRectHeight = clipRectHeight;
            this.flipHorizontally = flipHorizontally;
            this.flipVertically = flipVertically;
            this.scale = scale;
            this.color = color;
        }
        
        public void Draw(RenderThread renderThread)
        {
            image
                .getSubImage(this.clipRectX, this.clipRectY, this.clipRectWidth, this.clipRectHeight)
                .getFlippedCopy(flipHorizontally, flipVertically)
                .getScaledCopy(scale)
                .draw(x, y, color);
        }
    }
    
    private class DrawableString implements DrawableObject
    {
        private Font font;
        private float x;
        private float y;
        private String s;
        private Color color;
        
        public DrawableString(Font font, float x, float y, String s, Color color)
        {
            this.font = font;
            this.x = x;
            this.y = y;
            this.s = s;
            this.color = color;
        }
        
        public void Draw(RenderThread renderThread)
        {
            renderThread.graphics.setFont(this.font);
            renderThread.graphics.setColor(this.color);
            renderThread.graphics.drawString(this.s, this.x, this.y);
        }
    }
    
    private class DrawablePolygon implements DrawableObject
    {
        private Polygon polygon;
        private Color color;
        
        public DrawablePolygon(Polygon polygon, Color color)
        {
            this.polygon = polygon;
            this.color = color;
        }
        
        public void Draw(RenderThread renderThread)
        {
            renderThread.graphics.setColor(this.color);
            renderThread.graphics.draw(this.polygon);
        }
    }
    
    private class UseIntermediateRenderTarget implements DrawableObject
    {
        private Graphics intermediateRenderTarget;
        
        public UseIntermediateRenderTarget(Graphics intermediateRenderTarget)
        {
            this.intermediateRenderTarget = intermediateRenderTarget;
        }
        
        public void Draw(RenderThread renderThread)
        {
            Graphics.setCurrent(this.intermediateRenderTarget);
            this.intermediateRenderTarget.clear();
            renderThread.graphics = this.intermediateRenderTarget;
        }
    }
    
    private class FinishUsingIntermediateRenderTarget implements DrawableObject
    {
        public FinishUsingIntermediateRenderTarget()
        {
        }
        
        public void Draw(RenderThread renderThread)
        {
            Graphics.setCurrent(renderThread.defaultGraphics);
            renderThread.graphics = renderThread.defaultGraphics;
        }
    }
}
